package com.xu.cloud.common.processor;

import com.xu.cloud.common.constant.CryptConstant;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;

/**
 * @author xuguan
 * @since 2024/6/30
 */
@Configuration
public class DecryptionConfigurationPropertyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        final String encryptionKey = environment.getProperty(CryptConstant.ENCRYPTION_KEY_PROPERTY_NAME);
        final MutablePropertySources mutablePropertySources = environment.getPropertySources();
        for (PropertySource<?> propertySource : mutablePropertySources) {
            if (propertySource instanceof OriginTrackedMapPropertySource) {
                replaceDecryptionPropertySource(encryptionKey, (OriginTrackedMapPropertySource) propertySource, mutablePropertySources);
            }
        }
    }

    private static void replaceDecryptionPropertySource(String encryptionKey, OriginTrackedMapPropertySource propertySource, MutablePropertySources mutablePropertySources) {
        boolean needReplace = false;
        Map<String, Object> newPropertySourceMap = new HashMap<>(propertySource.getSource());

        for (String propertyName : propertySource.getPropertyNames()) {
            final Object property = propertySource.getProperty(propertyName);
            if (property instanceof String) {
                String propertyValue = property.toString();

                // 参数值解密
                final Matcher encryptionMatcher = CryptConstant.ENCRYPTION_PROPERTY_VALUE_PATTERN.matcher(propertyValue);
                while (encryptionMatcher.find()) {
                    if (encryptionKey == null) {
                        throw new IllegalArgumentException("encryption.key is not set");
                    }
                    needReplace = true;
                    String hit = encryptionMatcher.group();
                    final String actualEncryptionValue = hit.substring(CryptConstant.ENCRYPTION_PROPERTY_VALUE_PREFIX.length(), hit.length() - CryptConstant.ENCRYPTION_PROPERTY_VALUE_SUFFIX.length());
                    String decryptionProperty = DecryptionConfigurationPropertyHelper.decryptionProperty(actualEncryptionValue, encryptionKey);
                    propertyValue = propertyValue.replace(hit, decryptionProperty);
                }

                if (!Objects.equals(propertyValue, property.toString())) {
                    newPropertySourceMap.replace(propertyName, propertyValue);
                }
            }
        }

        if (needReplace) {
            // 替换原始的propertySource
            final Map<String, Object> unmodifiableNewPropertySourceMap = Collections.unmodifiableMap(newPropertySourceMap);
            OriginTrackedMapPropertySource newPropertySource = new OriginTrackedMapPropertySource(propertySource.getName(), unmodifiableNewPropertySourceMap, true);
            mutablePropertySources.replace(propertySource.getName(), newPropertySource);
        }
    }
}
